#include "win32/dkglMain_Win32.h"
#include "dkglKernel.h"
#include "dkglTimer.h"
#include <GL/gl.h>
#include <OIS.h>
#include <stdexcept>
#include <sstream>

namespace DKGL
{
	DKGLMainImpl::DKGLMainImpl()
	{
		constructW32Window();
	}

	DKGLMainImpl::~DKGLMainImpl()
	{
		
	}

	void DKGLMainImpl::registerKeyboardHandler(OIS::KeyListener* key)
	{
		_keyboard->setEventCallback(key);
	}

	void DKGLMainImpl::registerMouseHandler(OIS::MouseListener* mouse)
	{
		_mouse->setEventCallback(mouse);
	}

	void DKGLMainImpl::initOIS()
	{
		std::ostringstream wnd;
		wnd << (size_t)_hWnd;

		OIS::ParamList pl;
		pl.insert(std::make_pair( std::string("WINDOW"), wnd.str() ));
		//Default mode is foreground exclusive..but, we want to show mouse - so nonexclusive
		pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_FOREGROUND" )));
		pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_NONEXCLUSIVE")));

		_inputManager = OIS::InputManager::createInputSystem(pl);

		_keyboard = static_cast<OIS::Keyboard*>(
			_inputManager->createInputObject(OIS::OISKeyboard, true));
		
		_mouse = static_cast<OIS::Mouse*>(
			_inputManager->createInputObject(OIS::OISMouse, false));
	}
		
	void DKGLMainImpl::destroyOIS()
	{
		_inputManager->destroyInputObject(_keyboard);
		_inputManager->destroyInputObject(_mouse);
		OIS::InputManager::destroyInputSystem(_inputManager);
		_keyboard = NULL;
		_mouse = NULL;
		_inputManager = NULL;
	}


	void DKGLMainImpl::enableOpenGL()
	{
		int format;
		DWORD err_code;
		// set the pixel format for the DC
	
		PIXELFORMATDESCRIPTOR pfd = { 
			sizeof(PIXELFORMATDESCRIPTOR),		// size of this pfd 
			1,									// version number 
			PFD_DRAW_TO_WINDOW |				// support window 
			PFD_SUPPORT_OPENGL |				// support OpenGL 
			PFD_GENERIC_ACCELERATED|			// open OpenGL acceleration
		//	PFD_GENERIC_FORMAT|
			PFD_DOUBLEBUFFER,					// double buffered 
			PFD_TYPE_RGBA,						// RGBA type 
			24,									// 24-bit color depth 
			0, 0, 0, 0, 0, 0,			// color bits ignored 
			0,                     // no alpha buffer 
			0,                     // shift bit ignored 
			0,                     // no accumulation buffer 
			0, 0, 0, 0,            // accum bits ignored 
			16,                    // 16-bit z-buffer 
			0,                     // no stencil buffer 
			0,                     // no auxiliary buffer 
			PFD_MAIN_PLANE,        // main layer 
			0,                     // reserved 
			0, 0, 0                // layer masks ignored 
			}; //pfd
		
		format = ChoosePixelFormat( _hDC, &pfd );
				
		if(!SetPixelFormat( _hDC, format, &pfd ))
		{
			err_code = GetLastError();
			throw std::runtime_error("SetPixelFormat Fault!");
		}
		// create and enable the render context (RC)
		if((_hGLRC = wglCreateContext( _hDC ))==NULL)
		{
			throw std::runtime_error("Create Rendering Context Fault!");
		}

		wglMakeCurrent( _hDC, _hGLRC );
	}

	void DKGLMainImpl::disableOpenGL()
	{

	}

	void DKGLMainImpl::constructW32Window()
	{
		HINSTANCE	hInstance = GetModuleHandle(NULL);									// Grab An Instance For Our Window
		WNDCLASS	wndclass;
		HWND		hwnd;

#ifdef _UNICODE
		TCHAR		szAppName[] = L"DKGLApp";
#else
		char        szAppName[] = "DKGLApp";
#endif

		wndclass.style         = CS_HREDRAW | CS_VREDRAW;					// Redraw On Size, And Own DC For Window.
		wndclass.lpfnWndProc   = StaticWndProc;								// WndProc Handles Messages
		wndclass.cbClsExtra    = 0;											// No Extra Window Data
		wndclass.cbWndExtra    = 0;											// No Extra Window Data
		wndclass.hInstance     = hInstance;									// Set The Instance
		wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION);			// Load The Default Icon
		wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);				// Load The Arrow Pointe
		wndclass.hbrBackground = NULL;									// No Background Required For GL
		wndclass.lpszMenuName  = NULL;										// We Don't Want A Menu
		wndclass.lpszClassName = szAppName;									// Set The Class Name

		if (!RegisterClass(&wndclass))
		{
#ifdef _UNICODE
			MessageBox(NULL, L"Failed to register the window class.", L"ERROR", MB_OK);
#else
			MessageBox(NULL, "Failed to register the window class.", "ERROR", MB_OK);
#endif
		}

		hwnd = CreateWindowEx(
			WS_EX_APPWINDOW |
			WS_EX_WINDOWEDGE,			// Window Extended Style
			szAppName,									// Class Name
			szAppName,									// Window Title
			WS_OVERLAPPEDWINDOW | 
			WS_CLIPCHILDREN | 
			WS_CLIPSIBLINGS,								// Window Style
			CW_USEDEFAULT,
			CW_USEDEFAULT,							// Window Position
			CW_USEDEFAULT,									// Window Width
			CW_USEDEFAULT,									// Window Height
			NULL,											// No Parent Window
			NULL,											// No Menu
			hInstance,									// Instance
			this);

		if (!hwnd)
		{
#ifdef _UNICODE
			MessageBox(NULL, L"Window creation error.", L"ERROR" ,MB_OK);
#else
			MessageBox(NULL, "Window creation error.", "ERROR" ,MB_OK);
#endif
		}

		ShowWindow (hwnd, SW_SHOW);

		// init high resolution timer
		//LARGE_INTEGER ticksPerSecond;
		//QueryPerformanceFrequency(&ticksPerSecond);
		//mTicksPerSec = static_cast<float>(ticksPerSecond.QuadPart);
	}

	void DKGLMainImpl::go()
	{
		WINDOWPLACEMENT wndpl;
		MSG	msg;

		while (true)
		{
			if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
			{
				if (msg.message==WM_QUIT)
				{
					break;
				}
				else
				{
					TranslateMessage(&msg);
					DispatchMessage(&msg);
				}
			}

			Kernel::instance().frameStarted(0.1f);
			{
				Kernel::instance().update(0.1f);

				GetWindowPlacement(_hWnd, &wndpl);

				if ((wndpl.showCmd != SW_HIDE) &&
					(wndpl.showCmd != SW_MINIMIZE) &&
					(wndpl.showCmd != SW_SHOWMINIMIZED) &&
					(wndpl.showCmd != SW_SHOWMINNOACTIVE))
				{
					Kernel::instance().render(0.1f);
					SwapBuffers (_hDC);
				}
				Kernel::instance().frameEnded(0.1f);
			}

			if (_keyboard)
			{
				_keyboard->capture();
				//if( !_keyboard->buffered() )
				//	handleNonBufferedKeys();
			}

			if ( _mouse )
			{
				_mouse->capture();
				//if( !g_m->buffered() )
				//	handleNonBufferedMouse();
			}

			Sleep(1);
		}// Endof while
	}

	LRESULT DKGLMainImpl::WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
	{
		switch (msg)
		{
			case WM_CREATE:
				_hWnd = hwnd;
				_hDC = GetDC (hwnd);
				enableOpenGL();
				initOIS();
				GlobalTimer.start();
				return 0;
			case WM_SIZE:
				Kernel::instance().onWindowResize(
					LOWORD(lp), HIWORD(lp));
				return 0;
			case WM_DESTROY:
				disableOpenGL();
				destroyOIS();
				//OnDestroy();
				//KillTimer(hWnd, 0);
				PostQuitMessage(0);
				return 0;
		}
	}


	LRESULT CALLBACK DKGLMainImpl::StaticWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
	{
		DKGLMainImpl *win = NULL;

		if(msg == WM_CREATE)
		{
			// retrieve Window instance from window creation data and associate
			win = static_cast<DKGLMainImpl*>(((LPCREATESTRUCT)lp)->lpCreateParams);
			SetWindowLong(hwnd, GWL_USERDATA, reinterpret_cast<LONG>(win));
		}
		else
		{
			// retrieve associated Window instance
			win = reinterpret_cast<DKGLMainImpl*>(GetWindowLong(hwnd, GWL_USERDATA));
		}

		if (win)
			win->WndProc(hwnd, msg, wp, lp);

		return DefWindowProc(hwnd, msg, wp, lp);
	}
	
}